package lvm;

use File::Basename;

#########################################################################
# This module provides a series of routines to interface with the lvm	#
# subsystem - either the original lvm or lvm2/device mapper.		#
#########################################################################

sub new {
my $proto=shift;
my %args=@_;
my $class=ref($proto) || $proto;
my $self={};

	#################################################################
	# The first thing we do is to ascertain whether we have		#
	# /proc/lvm directory - or if not if /proc/misc contains a 	#
	# device-mapper entry.						#
	#################################################################

	if(-d "/proc/lvm") {
		$self->{TYPE}=1;
	}
	if(-f "/proc/misc") {
		my ($fd,$cline);
		open($fd,"/proc/misc");
		return undef if !defined($fd);
		while($cline=<$fd>) {
			$self->{TYPE}=2 if $cline =~ /device-mapper/;
		}
		close($fd);
	}
	return undef if !exists($self->{TYPE});

	bless($self,$class);
	return $self;
}

sub type {
my $self=shift;

	return $self->{TYPE};
}

sub has_commands {
my $self=shift;
my $p;

	if($self->{TYPE}==1) {
		$p=`which vgcreate 2>/dev/null`;
		chomp $p;
	} else {
		$p=`which lvm 2>/dev/null`;
		chomp $p;
	}
	if($p eq "") {
		return 0;
	}
	return 1;
}

sub activate_vgs {
	`/sbin/vgscan >/dev/null 2>&1; vgchange -ay >/dev/null 2>&1;`;
}

sub vg_list {
my $self=shift;
my %args=@_;
my ($fd,$cline,@list);

	if( (exists($args{UPTODATE}) && $args{UPTODATE} ==1) ||
		!exists($self->{VGLIST})) {
		`/sbin/vgscan >/dev/null 2>&1; vgchange -ay >/dev/null 2>&1;`;
		open($fd,"vgdisplay|");
		return undef if !defined($fd);
		while($cline=<$fd>) {
			chomp $cline;
			($cvg)=($cline =~ /^\s*VG Name\s+(\w+)$/);
			if(defined($cvg)) {
				push @list,$cvg;
				$cvg=undef;
			}
		}
		close($fd);
		$self->{VGLIST}=[@list];
	}
	return @{$self->{VGLIST}};
}

sub lv_list {
my $self=shift;
my %args=@_;
my ($fd,$cline,$vgname,@list,$clv);
my ($clvh,$x);

	if(!exists($args{VGNAME})) {
		return undef;
	}
	$vgname=$args{VGNAME};
	if( (exists($args{UPTODATE}) && $args{UPTODATE} ==1) ||
		!exists($self->{"LVLIST_$vgname"})) {
		open($fd,"vgdisplay -v $vgname 2>/dev/null|");
		return undef if !defined($fd);
		$clv=undef;
		$self->{"LVLIST_$vgname"}={};
		while($cline=<$fd>) {
			chomp $cline;
			if($cline =~ /LV Name/) {
				($clv)=($cline =~ /^\s*LV Name\s+(.*)/);
				if(defined($clv)) {
					$clv=basename($clv);
				}
				$clvh={};
				$clvh->{NAME}=$clv;
				next;
			}
			if($cline =~ /LV Size/) {
				($x)=($cline =~ /^\s*LV Size\s+([0-9\.]+)\s+/);
				if(defined($x)) {
					$clvh->{SIZE}=sprintf("%d",$x*1024);
					# If line contains GB then multiply again!
					$clvh->{SIZE}*=1024 if $cline =~ /GB/;
				}
				next;
			}
			if($cline =~ /# open/) {
				($x)=($cline =~ /^\s*# open\s+([0-9]+)$/);
				if(defined($x)) {
					$clvh->{OPEN}=$x;
				}
				next;
			}
			if($cline =~ /LV Status/) {
				($x)=($cline =~ /^\s*LV Status\s+(\w+)/);
				if(defined($x)) {
					$clvh->{STATUS}=$x;
				}
				next;
			}
			if(defined($clv) && $cline =~ /^\s*$/) {
				$self->{"LVLIST_$vgname"}->{$clv}=$clvh;
			}
		}
		close($fd);
	}

	#################################################################
	# Use the information in the LVLIST_vgname hash to return	#
	# a list of lvnames.						#
	#################################################################

	return (keys(%{$self->{"LVLIST_$vgname"}}));
}

sub get_lvinfo {
my $self=shift;
my %args=@_;
my ($lvname,$vgname);

	if(!exists($args{VGNAME}) || !exists($args{LVNAME})) {
		return undef;
	}
	$vgname=$args{VGNAME};
	$lvname=$args{LVNAME};
	if( (exists($args{UPTODATE}) && $args{UPTODATE} ==1) ||
		!exists($self->{"LVLIST_$vgname"})) {
		lv_list($self,(VGNAME => $vgname));
	}
	if(!exists($self->{"LVLIST_$vgname"})) {
		return undef;
	}
	if(!exists($self->{"LVLIST_$vgname"}->{$lvname})) {
		return undef;
	}

	return ($self->{"LVLIST_$vgname"}->{$lvname}->{STATUS},
		$self->{"LVLIST_$vgname"}->{$lvname}->{SIZE},
		$self->{"LVLIST_$vgname"}->{$lvname}->{OPEN}
		);
}

sub lvcreate {
my $self=shift;
my %args=@_;
my ($lvname,$vgname,$size,$r,$cmd);

	if(!exists($args{VGNAME}) || !exists($args{LVNAME}) ||
		!exists($args{SIZE})) {
		return -1;
	}
	$vgname=$args{VGNAME};
	$lvname=$args{LVNAME};
	$size=$args{SIZE}/1024;
	@r=get_lvinfo($self,@_);
	if(defined($r[0])) {
		return -2;
	}
	if($size<=0) {
		return -3;
	}
	$cmd="lvcreate -n $lvname -L $size ";
	if($self->{TYPE}==1) {
		$cmd.="/dev/$vgname";
	} else {
		$cmd.="$vgname";
	}
	$r=`$cmd 2>&1 >/dev/null`;
	if($?>>8) {
		chomp $r;
		return (-4,$r);
	}
	return 0;
}

sub lvresize {
my $self=shift;
my %args=@_;
my ($lvname,$vgname,$size,$r,$cmd);

	if(!exists($args{VGNAME}) || !exists($args{LVNAME}) ||
		!exists($args{SIZE})) {
		return -1;
	}
	$vgname=$args{VGNAME};
	$lvname=$args{LVNAME};
	$size=$args{SIZE}/1024;
	@r=get_lvinfo($self,@_);
	if(!defined($r[0])) {
		return -2;
	}
	if($size<=0) {
		return -3;
	}
	if($size*1024<$r[1]) {
		$cmd="lvreduce -L $size -f /dev/$vgname/$lvname 2>&1 >/dev/null";
	}
	if($size*1024>$r[1]) {
		$cmd="lvextend -L $size /dev/$vgname/$lvname 2>&1 >/dev/null";
	}
	if(!defined($cmd)) {
		return 0;
	}
	$r=`$cmd`;
	if($?>>8) {
		chomp $r;
		return (-4,$r);
	}
	return 1;
}

sub vg_activate {
my $self=shift;
my %args=@_;
my $vg;
	if(!exists($args{VGNAME})) {
		return -1;
	}
	$vg=$args{VGNAME};

	if($self->{TYPE}==1) {
		return 1;
	}
	if(has_commands($self)==0) {
		return -2;
	}
	`vgchange -a y $vg >/dev/null 2>/dev/null`;
	if($?>>8==0) {
		return 1;
	}
	return 0;
}

1;


